﻿#region License

// Another free, open solution by someone who gets it.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;

using AutoMapper;

using Patterns.Collections;
using Patterns.Xml;

namespace Patterns.Mapping
{
	/// <summary>
	/// 	Defines extensions for mapping to and from various types.
	/// </summary>
	public static class MappingExtensions
	{
		private const BindingFlags _flags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		/// <summary>
		/// 	Converts from a dictionary to a strongly-typed value.
		/// </summary>
		/// <typeparam name = "T"></typeparam>
		/// <param name = "expression">The expression.</param>
		/// <returns></returns>
		public static IMappingExpression<IDictionary, T> ConvertFromDictionary<T>(this IMappingExpression<IDictionary, T> expression)
		{
			FindProperties(typeof (T))
				.Each(key => expression.ForMember(key.Name, config => config.MapFrom(values => values.Pull(key))));

			return expression;
		}

		/// <summary>
		/// 	Converts from xml to a strongly-typed value.
		/// </summary>
		/// <typeparam name = "T"></typeparam>
		/// <param name = "expression">The expression.</param>
		/// <returns></returns>
		public static IMappingExpression<XElement, T> ConvertFromXml<T>(this IMappingExpression<XElement, T> expression)
		{
			FindProperties(typeof (T))
				.Each(key => expression.ForMember(key.Name, config => config.MapFrom(xml => xml.GetValue(key.Name))));

			return expression;
		}

		/// <summary>
		/// 	Finds the properties.
		/// </summary>
		/// <param name = "containerType">Type of the container.</param>
		/// <param name = "flags">The flags.</param>
		/// <param name = "propertyFilter">The property filter.</param>
		/// <returns></returns>
		public static IEnumerable<PropertyInfo> FindProperties(this IReflect containerType, BindingFlags flags = _flags, Func<PropertyInfo, bool> propertyFilter = null)
		{
			propertyFilter = propertyFilter ?? (property => property.CanRead && property.CanWrite);
			PropertyInfo[] properties = containerType.GetProperties(flags);
			return properties.Where(p => propertyFilter(p));
		}
	}
}